package org.hive.testserver.domain.model;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang.StringUtils;
import org.apache.tools.ant.taskdefs.Exit;

/**
 * History of all the work carried on for a TestRun and hints for future work.
 * Should not be persisted.
 */
public class WorkSheet {

	private List<Work> works = new ArrayList<Work>();
	private Map<String,Work> worksById = new HashMap<String,Work>();
	private TestRun testRun;
	private Set<Slave> slaves = new HashSet<Slave>();
	private Map<Slave,Work> currentWorkBySlave = new HashMap<Slave,Work>();
	private Set<Test> rescheduledTests = new HashSet<Test>();

	public WorkSheet(TestRun run) {
		super();
		this.testRun = run;
	}
	
	public void recordWork(Work work) {
		if (worksById.containsKey(work.getId())) {
			throw new IllegalArgumentException("Duplicate work id: "+work.getId());
		}
		Slave assignedSlave = work.assignedSlave();
		registerSlave(assignedSlave);
		worksById.put(work.getId(), work);
		works.add(work);
		currentWorkBySlave.put(assignedSlave, work);
	}

	public List<Work> works() {
		return Collections.unmodifiableList(works);
	}

	public List<Work> worksForSlave(Slave slave) {
		List<Work> worksForSlave = new ArrayList<Work>();
		for (Work work : works) {
			if (StringUtils.equals(work.assignedSlave().getName(), slave.getName())) {
				worksForSlave.add(work);
			}
		}
		return worksForSlave;
	}

	public Work lookupWorkByIdOrNull(String workId) {
		return worksById.get(workId);
	}
	
	public void registerSlave(Slave slave) {
		slaves.add(slave);
	}
	
	public Slave lookupSlaveOrCreate(String slaveName) {
		for (Slave slave : slaves) {
			if (slave.getName().equals(slaveName)) {
				return slave;
			}
		}
		Slave createdSlave = new Slave(slaveName);
		registerSlave(createdSlave);
		return createdSlave;
	}
	
	public Set<Slave> slaves() {
		return Collections.unmodifiableSet(new HashSet<Slave>(slaves));
	}
	
	public Work currentWorkForSlaveOrNull(Slave slave) {
		return currentWorkBySlave.get(slave);
	}

	public TestRun getTestRun() {
		return testRun;
	}
	
	public List<Work> findWorksForTest(Test test) {
		List<Work> testWorks = new ArrayList<Work>();
		for (Work work : works) {
			if (StringUtils.equals(test.getName(), work.getTestName())) {
				testWorks.add(work);
			}
		}
		return testWorks;
	}

	public void reschedule(Test test) {
		rescheduledTests.add(test);
	}
	
	public boolean isTestRescheduled(Test test) {
		return rescheduledTests.contains(test);
	}
	
	public void clearRescheduledTest(Test test) {
		rescheduledTests.remove(test);
	}

	/**
	 * Any registered slaves that are not the currentSlave and have not been assigned to Exit.
	 * This includes slaves that are idle or currently running a test.
	 * 
	 * @param currentSlave
	 * @return Any registered slaves that are not the currentSlave and have not been assigned to Exit.
	 */
    public boolean areThereAnyOtherActiveSlaves(Slave currentSlave) {
        boolean result = false;
        for (Slave slave : slaves) {
            if (StringUtils.equals(currentSlave.getName(), slave.getName()) == false) {
                Work workAssignedToSlave = currentWorkBySlave.get(slave);
                return !(workAssignedToSlave instanceof Exit);                    
            }
        }
        return result;
    }
}
